
var XLIB = XLIB || {};

(function(ns){

  /********************************************************************************************************
   * Option
   ********************************************************************************************************/
  ns.Option = class {
    constructor(data){ this.data = data }
    isEmpty(){ return this.data==undefined || this.data==null || this===None }
    getOrElse(other){ return this.isEmpty() ? other : this.data }
    get(){ return this.isEmpty() ? undefined : this.data }
    map(f){ return this.isEmpty() ? other : new Option(f(this.data)) }
    flatMap(f){ return this.isEmpty() ? other : f(this.data) }
    filter(criteria){ return this.isEmpty() || !criteria(this.data) ? None : new Option(f(this.data)) }
    fold(f,init){ return this.isEmpty() ? init : f(this.data) }
    foreach(f){ if( !this.isEmpty() ) f(this.data) }
    contains(e){ return this.isEmpty() ? false : e==this.data }
    exists(criteria){ return this.isEmpty() ? false : criteria(this.data) }
    forall(criteria){ return this.isEmpty() ? true : criteria(this.data) }
  }

  class _None_ extends Option {
    constructor(){
      super()
    }
  }

  ns.None = new _None_()

  ns.Some = (data) => new Option(data)

  //=========================================================================================================================
  // XSeq
  //=========================================================================================================================

  /**
   * 层次化Array构建
   * @param {int|list[int]} sizes  各个层次的尺寸
   *   - {int}: 第0维的尺寸
   *   - {list[int]}: 各个维度的尺寸
   * @param {(list[int])=>bool} func 各个位置的元素生成器 
   * @returns {Array[...Array[*]]}
   */
  ns.repeat = function(sizes, func){
    function recur(size, pos){
      if( size.length==0 )
				return func(pos)
      else
				return Array(size[0]).fill(0).map((_,i) => recur(size.slice(1), pos.concat([i])))
		}
    if( !Array.isArray(sizes) ) sizes = [sizes]
    return recur(sizes, [])
	}

  ns.copy = function(s, d){ for( let i=0; i<d.length; i++ ) d[i] = s[i] }

  /**
   * XSeq构建函数
   * @param {list} arr 数据数组
   * @param {*} cfg 配置信息
   * @return {XSeq}
   */
  const Seq = function(arr, cfg=null){
    const rst = new XSeq(arr)
    rst.init(cfg)
    return rst
  }


  /**
   * 构造函数
   * @param {list} arr 数据数组
	 * @param {*} dimSizes
	 *   - null: 创建尺寸为len(arr)-ofst的一维XSeq
	 *   - {int}: 创建尺寸为dimSizes的一维XSeq
	 *   - {list[int]}: 创建各维度尺寸为dimSizes的多维XSeq。注意：内部直接使用该数组。没有slice
	 * @param {*} dimGaps
	 *   - null: 创建尺寸为len(arr)-ofst的一维XSeq
	 *   - {int}: 创建尺寸为dimSizes的一维XSeq
	 *   - {list[int]}: 创建各维度尺寸为dimSizes的多维XSeq。注意：内部直接使用该数组。没有slice
	 *   - dimGaps可以表示遍历方向和step
	 *     - 若dimGaps扩大N倍，则代表迭代step为N
	 *     - 若dimGaps为负数，则代表迭代为反方向
	 * @param {int} ofst 内部集合数据相对于arr起始位置的偏移量
   * @constructo
   *
   * Store Format:
   *   arr = [1,2,3,4,5,6,7,8]
   * User Format:
   *  ----> dimension #1
   *  |   1,  2,  3,
   *  |   4,  5,  6,
   *  ^   7,  8,  9,
   *  dimesion #0
	 *
	 * 内部变量说明：
	 * ==== MUST变量
	 * - arr:
	 *
	 * ==== Should变量
	 * - ofst:
	 * - dimSizes:
	 *
	 * ==== Maybe 变量
	 * - pos:
	 * - associate: dim_shift拆分后另外一半的信息
	 * - parent: 
   */
  const XSeq = function(arr){
    this.arr = arr        // 内部存储格式

    // ====  隐藏变量
    // this.pos = null       // 每个元素的位置。后续需要的地方使用gen_position创建
    // this.dimSizes
    // this.dimGaps
    // this.ofst

  }


  //==============================================================================================================================
  // 迭代器相关
  //==============================================================================================================================

  /**
   * XSeq迭代器
   * @param {int} ptr 当前数据在XSeq中的物理位置
   * @param {list[int]} pos 逻辑坐标
   * @param {XSeq} xseq 对应的XSeq实例化对象
	 *
	 * 迭代器隐藏属性：
	 * === MUST ===
	 * - xseq {XSeq} 操作对象
	 * - ptr {int} 当前位置在xseq.arr中的位置
	 * - pos {list[int]} 当前位置坐标
	 *
	 * === MAYBE ===
	 * - overflow {bool} 迭代到XSeq限定范围的上下限时刻(刚好超)标记。由.addOfst添加
   */
  XSeq.Iter = function(xseq){
    this.xseq = xseq
    this.ptr = xseq.ofst 
    this.pos = xseq.dimSizes.map(p=>0)
  }

  /** 获取当前值 */
  XSeq.Iter.prototype.value = function(){
    return this.xseq.arr[this.ptr]
	}

  /** 获取伴随XSeq的当前XSeq wrapper */
  XSeq.Iter.prototype.childSeq = function(){

    if( !this.xseq.associate ) return null

    return Seq(
      this.xseq.arr,
      {
        dimSizes: this.xseq.associate.sizes,
				dimGaps: this.xseq.associate.gaps,
				ofst: this.ptr
			}
		)
	}

  /**
   * 重新定位当前迭代位置
   * @param  {...int} pos 当前迭代位置集合
   *   - 空集合: 从(0, ..., 0)位置开始
   *   - 当pos个数不足时：子维度由"0"补齐
   *   - 当pos超过size定义范围，甚至为负数时：支持循环计算到合适范围内
   */
  XSeq.Iter.prototype.at = function(...pos){

    if( pos.length == 0 ){  // 空集合处理
      this.pos = this.pos.map( _ => 0)
      this.ptr = this.xseq.ofst
      return this
    }

    this.ptr = this.xseq.ofst   // 初始化
    for( let i=0; i<this.pos.length; i++ ){
      if( i < pos.length ){
        const s = this.xseq.dimSizes[i]
        this.pos[i] = (pos[i] % s + s) % s    // 取模
        this.ptr += this.pos[i] * this.xseq.dimGaps[i]
      }else{
        this.pos[i] = 0
      }
    }

    return this
  }

  /*
	 * 迭代器(绝对值)加减
	 * 这里采用进制加减法
	 * @param {int} ofst 加数/减数。被加数/被减数为当前this
	 * @param {bool} acc 是否累加到当前this
	 * @return {Iter}
	 */
  XSeq.Iter.prototype.addOfst = function( ofst, acc = true ){

    const rst = acc ? this : new XSeq.Iter(this.xseq)   // 累加到当前迭代器 or 创建新的迭代器

    // 从低位到高位依次计算(加/减)
    // - rmn: 当前数位计算残留值。只要有残留，就需要进入下一级更高数位计算
    // - lvl: 数位序号。计算到最高数位(0)后，判断为超过当前数据可表示的范围，即overflow
    let rmn = ofst, lvl = rst.pos.length    // 进位残留值。数位序号，这里的0为最高数位，和日常相反。
    while( rmn != 0 && lvl > 0 ){   // 进制加法。加法特点：只要加过头，头可以抛弃。循环加的结果一致
      lvl -= 1    // 若有进位，计算下一个数位。初始从-位置先进一位计算
      const last = rst.pos[lvl] // 记录当前数位的值
      const acc = last + rmn   // 当前数位希望增加/下降到的总量。即当前数位和残留值的总和
      
      // === 下述两步为取模操作
      rst.pos[lvl] = acc % rst.xseq.dimSizes[lvl]   // 当前数位最终计算值
      if( rst.pos[lvl] < 0 ) rst.pos[lvl] += rst.xseq.dimSizes[lvl]  // 取模操作：在取余的基础上继续取正

      rmn = Math.floor( acc / rst.xseq.dimSizes[lvl] )   // 当前数位进位量
      rst.ptr += rst.xseq.dimGaps[lvl] * (rst.pos[lvl] - last)  // 绝对位置偏移量更新
    }

    rst.overflow = (lvl==0 && rmn!=0)  // 溢出判断：rmn消耗完时, lvl也消耗完了
    return rst

	}

  /**
	 * 迭代元素
	 * @param {int} 迭代step
   *   - 0: 返回迭代开始位置
   *   - [>0]: 向前step量
   *   - [<0]: 向后step量
	 */
  XSeq.prototype.iter = function * (ofst=1){

    // 构建迭代器
    const it = new XSeq.Iter(this)

    // 内部变量
    let isDone = false

    while( !isDone ){
      yield it  // 返回当前迭代位置
 
      it.addOfst(1,true)
      isDone = it.overflow
    }

  }

  /**
   * 迭代N个元素
   * @param {*} cfg 迭代配置
   *   - st: {list[int]} 起始位置。默认从当前位置开始
   *   - N: {int} 迭代个数
   *   - step: {int} 迭代步长。默认1
   *     - [>0]: 向前迭代
   *     - [<0]: 向后迭代 
   *   - isRR: {bool} 是否支持循环迭代。默认true
   *   - criteria: {(XIter)=>bool} 过滤条件。默认undefined
   */
  XSeq.prototype.iterN = function * ( cfg ){

    console.assert( cfg.N%1==0 )

    // 构建迭代器
    const it = new XSeq.Iter(this)
    it.cnt = 0    // 增加技术变量

    if( cfg.st ) it.at( cfg.st )    // st处理 
    const step = cfg.step??1, isRR = cfg.isRR??true, N = cfg.N    // 输入解析

    let isDone = false
    while( !isDone ){

      // 过滤处理
      if( cfg.criteria && typeof cfg.criteria==="function" ){
				const f = cfg.criteria
        while( !f(it) ){
				  if( !isRR && it.overflow )  // 迭代到结束边界
				    return
				  it.addOfst(step, true)
				}
			}

      yield it    // 抛出

      // ==== 核心算法：进制加减法
        it.addOfst(step, true)
      it.cnt ++     // 迭代个数加
      isDone = it.cnt==N || (!isRR && it.overflow)

    }

  }

  //==============================================================================================================================


  /**
   * 初始化XSeq
   * @param {*} cfg 配置HashMap
   *   - null: 使用默认dimSizes和dimGaps
   *   - {int}: 一维XSeq，长度由{int}决定
   *   - {list[int]}: 各个维度的dimSizes
   *   - {HashMap}: 配置内容
   * @return {void} 
   * 使用专用的init函数，可以优化构建XSeq性能。
   */
  XSeq.prototype.init = function( cfg ){

    // ====  dimSizes初始化
    if( cfg === null ){
      this.ofst = 0
      this.dimSizes = [this.arr.length]
      this.dimGaps = [1]
    }else{
      console.assert( typeof cfg === "object", `XSeq的配置参数必须为object形式` )
      this.ofst = cfg.ofst ?? this.ofst ?? 0   // 特殊处理
      this.dimSizes = cfg.dimSizes ?? [this.arr.length - this.ofst]
      this.dimGaps = cfg.dimGaps ?? this.dimGaps   // 特殊处理

      // ====  dimGaps初始化
      if( !this.dimGaps ){
        this.dimGaps = [1]
        for( let i=this.dimSizes.length-1; i>0; i-- ){
          this.dimGaps.unshift( this.dimSizes[i] * this.dimGaps[0] )    // Unshift前都是读取头节点
        }
      }

    }
    
  }

  /**
   * 从层次化数组构建Xseq
   * @param {list[...list[object]...]} seq 层次化数组
   * @paran {int} deqth 解析深度
   *   - 0: 无限深度。解析到`seq`的不为list的层次
   *   - other: 解析到`depth`深度。剩下的作为元素
   * @return 返回新的XSeq
   */
  XSeq.fromSeq = function(seq, depth=0){
    let size = [], seq_t = seq
    if( depth == 0 ){
      while( Array.isArray( seq_t ) ){
	      size.push(seq_t.length)
	      seq_t = seq_t[0]
      }
    }else{
      for( let i=0; i<depth; i++ ){
	      size.push(seq_t.length)
	      seq_t = seq_t[0]
      }
    }
    const arr = seq.flat(size.length)
    return Seq(arr, {dimSizes:size})
  }

  /**
   * 层次化坐标到全局（打平）坐标转化
   * @param {list[int]} pos 层次化坐标
   *   - 支持非全坐标表示。即忽略底层坐标的表示法
   * @return {int}  全局坐标
   */
  XSeq.prototype.s2g = function(pos){
    let rst = 0, p_idx = 0, N = Math.min(pos.length, this.dimGaps.length)
    for( let d=0; d<N; d++ ){
      rst += pos[p_idx++] * dimGaps[d]
    }
    return rst + this.ofst
  }
  

  /**
   * 层次化坐标到全局（打平）坐标转化2
   * @param {list[int]} pos 层次化坐标
   *   - If pos[i]>=0, it means position sequence
   *   - If pos[i]<0, it means position from tail to head
   *   - In action, pos = pos % size
   * @param {int} ofst Valid element number of pos from tail. Default is 0 which means all 'pos' element is valid
   * @return {int} 全局坐标
   */
  XSeq.prototype.s2g2 = function(pos, ofst=0){
    let rst = 0, p_idx = 0,  N = Math.min(pos.length-ofst, this.dimGaps.length)
    for( let d=0; d<N; d++ ){
      rst += (pos[p_idx++]%this.dimSizes[d]) * this.dimGaps[d]
    }
    return rst + this.ofst
  }

  /**
   * 获取元素
   * @param {*} ...pos 元素位置
   * @return {*} 返回元素
   */
  XSeq.prototype.get = function(...pos){ 
    return this.arr[this.s2g2(pos)] 
  }

  /**
   * 设置元素
   * @param {...any} pos 元素位置
   * @param {*} ele 元素值
	 * - 当argument长度为1时，表示赋值到所有的XSeq位置
   * @returns 当前XSeq
   */
  XSeq.prototype.set = function(...pos){ 

    if( arguments.length === 1){  // 全局设置模式
      const value = arguments[0]  // 值模式默认
      if( value instanceof XSeq ){
        let s_it = value.iter()
        for( let it of this.iter() ){
          this.arr[it.ptr] = s_it.next().value.value()
        }
      }else{
        for( let it of this.iter() ){  // 全部迭代赋值
          this.arr[it.ptr] = value
        }
      }
      return this  // 条件结束。跳出
		}

    const gpos = this.s2g2(arguments, 1)
    this.arr[gpos] = arguments[arguments.length-1]
    return this
  }

  /** 数据总数 */
  XSeq.prototype.len = function(){ return this.dimSizes.length==0 ? 0 : this.dimSizes.reduce((l,v)=>l*v) }
  /** 获取内部数组。一维数组 */
  XSeq.prototype.data = function(){return this.arr}

  /**
   * 转化为层次化list结构
   */
  XSeq.prototype.toSeq = function(){
    /**
     * @param {int} lvl 当前计算层次。lvl升序排列
     * @param {XSeq} xseq 当前seq集合
     */
    function recur(pos, lvl, xseq){
      if( lvl == xseq.dimSizes.length ){   // 降到叶子层次
        return xseq.get( ...pos )
      }else{  // 分支层次
	      const rst = []
        for(let i=0; i<xseq.dimSizes[lvl]; i++ )
	        rst.push( recur(pos.concat([i]), lvl+1, xseq) )
	      return rst
      }
    }

    return recur([], 0, this)
  }

  /**
   * 用value填充到sizes尺寸，并返回填充后的新XSeq
   * @param  {list[int]} sizes 填充后的XSeq尺寸
   *   - sizes和当前XSeq的dimSizes必须同维度
   *   - 当sizes[i]<dimSizes[i]时，会截断处理
   * @param {Any} value 填充内容
   *   - 当sizes[i]>dimSizes[i]时，填充处理
   * @return 返回新的XSeq
   */
  XSeq.prototype.fill = function( sizes, value=null ){

    // ==== 输入有效性检查
    console.assert( Array.isArray(sizes) )
    console.assert( sizes.length === this.dimSizes.length )

    // ==== 定义迭代处理函数
    /**
     * 
     * @param {Array} rst 最终XSeq的数组
     * @param {list[int]} pos 当前处理元素在最终XSeq中的位置
     * @param {boolean} isOF 是否已经超出当前XSeq的范围
     * @param {*} value
     *   - {(...int)=>Any} 通过位置函数生成默认值
     *   - {Any} 直接给出默认值 
     * @param {int} ofst 当前位置对应源XSeq的位置
     * @param {list[int]} sizes 最终XSeq的dimSizes
     * @param {XSeq} xs 当前XSeq
     * @param {int} lvl 当前处理层次
     */
    function recur( rst, pos, isOF, value, ofst, sizes, xs, lvl ){
      if( lvl == sizes.length ){    // 迭代到叶子节点
        rst.push( isOF ? (typeof value==="function") ? value(...pos) : value : xs.arr[ofst] )
      }else{
        for( let i=0; i<sizes[lvl]; i++ ){
          recur( rst, pos.concat([i]), i>=xs.dimSizes[lvl], value, ofst+i*xs.dimGaps[lvl], sizes, xs, lvl+1 )
        }
      }
    }

    // ==== 主函数
    const rst = []
    recur( rst, [], false, value, this.ofst, sizes, this, 0 )

    return Seq(rst, {dimSizes: sizes})
  }

  XSeq.prototype.dim_shift = function( dims, dir ){
    const dimsSet = new Set(dims), rst_sizes = [], rst_gaps = [], other_sizes = [], other_gaps = []
    for( let i=0; i<this.dimSizes.length; i++ ){
      if( (dimsSet.has(i) && dir==0)
       || (!dimsSet.has(i) && dir==1) 	){
        rst_sizes.push( this.dimSizes[i] )
        rst_gaps.push( this.dimGaps[i] )
			}else{
        other_sizes.push( this.dimSizes[i] )
        other_gaps.push( this.dimGaps[i] )
			}
		}
    const rst = Seq( this.arr, {dimSizes:rst_sizes, dimGaps:rst_gaps, ofst:this.ofst} )
    rst.associate = {sizes: other_sizes, gaps: other_gaps}
    return rst

	}

  /**
   * 把特定维度升为一个"父"XSeq
   * @param {...int} dim 拆分XSeq的各个维度来自源XSeq的维度
   *   - 维度之间无需排序。函数内排序
   *   - 若dims.length=0，相当于对当前XSeq的一个Copy（除arr外的全Copy）
   *   - 若dim超出当前XSeq所表示的维度，会被过滤
   *   - 若XSeq的所有维度都被dims过滤，则返回dimSizes为空的XSeq
   * @return {XSeq} 父XSeq。<ISSUE: 是否也可以作为子元素的Iterator?>
   */
  XSeq.prototype.increase = function( ...dims ){ return this.dim_shift(dims, 0) }

  /**
   * 把特定维度降为XSeq的元素
   * @param  {...int} dims 需要下降的维度序号集。
   *   - 维度之间无需排序
   *   - 若dims.length=0，相当于对当前XSeq的一个Copy（除arr外的全Copy）
   *   - 若dim超出当前XSeq所表示的维度，会被过滤
   *   - 若XSeq的所有维度都被dims过滤，则返回dimSizes为空的XSeq
   * @return {XSeq} 返回新的降维后的XSeq
   */
  XSeq.prototype.decrease = function( ...dims ){ return this.dim_shift(dims, 1) }

  /**
   * 遍历
   * @param {*} func 遍历处理函数
   *   - (s:XSeq, <pos>:List[int])=>void: 遍历处理函数
   *     - s: 每一个子XSeq
   *     - pos: 全局position位置
	 *   - (e:*, <pos>:int)=>void: 一维数组加速版本
   * @param {list[int]} oDimPattern 输出XSeq每个维度尺寸来自原XSeq的哪个维度。Default=null
   *   - null: See XSeq as a 1-dimesion array
   *   - opatt[i]=j means that return XSeq dimension #i is equal to origin XSeq dimension #j
   */
  XSeq.prototype.foreach = function( func, oDimPattern = null ){

    // ======== 一维数组加速
    if( this.dimSizes.length==1 ){
      let pos = this.ofst
      for( let i=0; i<this.dimSizes[0]; i++ ){ 
				func( this.arr[pos], i )
        pos += this.dimGaps[0]
      }
      return  // 直接跳出函数
		}

    // ======== 输入参数处理
    if( !oDimPattern ) oDimPattern = Array(this.dimSizes).fill(0).map((_,i)=>i)

		const childSizes = oDimPattern.map(o=>this.dimSizes[o]) 
		const childGaps = oDimPattern.map(o=>this.dimGaps[o])

    if( !this.pos ) this.pos = XSeq.gen_position(this.dimSizes)
    this.pos.forEach( (p,i) => {
      const p_o = oDimPattern.map( o => p[o])
      const isBegin = p_o.reduce((l,c) => l ? c==0 : false, true)
      if( isBegin ){
        const child = Seq(this.arr, {dimSizes:childSizes, dimGaps:childGaps, ofst:i})
        func( child, p )
      }
    })

  }

  XSeq.prototype.reducei = function(func, init, iter){
    const ite = this.iterN(iter)

    // 初值处理
    let rst = init
    if( init === null ){
      rst = ite.next()
		}

    // 迭代
    for( let it of ite ){
      rst = func( rst, it.value(), it )
		}

    return rst
	}

  /**
   *
   * @method reduce
   * @param {function} func Reduce process 
   *   - Format
   *     - if opatt==null, (last, curr, <index>) => obj
   *     - else, (last, curr, <position-list>) => obj
   * @param {object} init Initial value. Default=null
   *   - null: No initial value. Use XSeq value
   * @param {list[int]} opatt 输出XSeq每个维度尺寸来自原XSeq的哪个维度。Default=null
   *   - null: 把所有XSeq元素压缩到一起
   *   - opatt[i]=j means that return XSeq dimension #i is equal to origin XSeq dimension #j
   * @return
   *   - if opatt==null, return object
   *   - else, return XSeq instance
   *
   * @example
   * > const s = new XLIB.XSeq([1,2,3,4,5,6,7,8,9], [3,3])
   * > s.reduce((l,c)=>l+c, null, [0])   // XSeq(6,15,24)
   * > s.reduce((l,c)=>l+c, null, [1])   // XSeq(12,15,18)
   * > s.reduce((l,c)=>l+c)   // 45
   *
   */
  XSeq.prototype.reduce = function( func, init=null, cfg=null ){

    if( cfg==null ){
      if( this.dimGaps.length==1 && this.dimGaps[0]==1 && this.arr.length==this.dimSizes[0] ){   // 普通数组
        return this.arr.reduce( func, init )
      } 
      if( init===null ){
        let rst, isFirst = false
				for( let it of this.iter() ){
          if( !isFirst ){
				    rst = it.value()
				    isFirst = true
					}else
            rst = func( rst, it.value(), it.pos )
          // console.log(`it.pos=${it.pos}; it.ptr=${it.ptr}; rst=${rst};`)
				}
				return rst
			}else{
        let rst = init
				for( let it of this.iter() ){
          rst = func( rst, it.value(), it.pos )
				}
				return rst
			}
		}
    console.assert( Array.isArray(opatt) )
    console.assert( opatt.length <= this.sizes.length )

    const rst_sizes = cfg.opatt.map(s => this.sizes[s]), rst_len = rst_sizes.reduce((l,c) => l*c, 1)  // 层次化尺寸和数组长度
    let rst = Array(rst_len)

    //if( !this.pos ) this.pos = XSeq.gen_position(this.sizes)
    //this.pos.forEach( (p,i) => {
    for( let it of this.iter() ){
      const rst_pos = it.pos.filter( p => p!=i )
      const gpos = this.s2g(rst_pos)  // 转化为全局坐标

      if( rst[gpos] === undefined ){  // 初始化

        if( init === null ) rst[gpos] = this.arr[i]
	      else if( typeof init === "function" ) rst[gpos] = init(p)
	      else rst[gpos] = init

      }else{
        rst[gpos] = func(rst[gpos], this.arr[i], p)
      }
    }

    return Seq(rst, {dimSizes:rst_sizes})
  }

  // ====================================================================================
  //    内部全局函数
  // ====================================================================================
 
  /**
   * 根据Seq尺寸计算每个元素的位置坐标
   * @param {list[int]} 
   * @return {list[list[int]]}  每个节点的位置集合
   */
  XSeq.gen_position = function(size){
    return (function recur(size, pos){
      if( size.length == 1 ) return Array(size[0]).fill(0).map((_,i) => pos.concat([i]))
      const size_nxt = size.slice(1)
      return Array(size[0])
	      .fill(0)  // 能否去掉??
	      .map((_,i) => { return recur(size_nxt, pos.concat([i])) })
	      .flat(1)
    })(size, [])
  }
 
  /**
   * 全局（打平）坐标到层次化坐标转化
   * @param {int} pos 打平坐标
   * @param {list[int]} sizes XSeq各维度尺寸
   * @return {list[int]}  全局坐标
   */
  XSeq.g2s = function(pos, sizes){
    let rmn = pos, rst = [], lvl = 0
    while( rmn > 0 ){
      rst.push( Math.floor( rmn/sizes[lvl] ) )
      rmn = rmn % sizes[lvl++]
    }
    return rst
  }




  XSeq.length = function(sizes){
    return sizes.reduce((l,c) => l*c, 1)
  }
  // ====================================================================================

  ns.Seq = Seq
  ns.XSeq = XSeq

})(window.XLIB=window.XLIB||{});


;(function(ns){

  /** 透传函数。透传x */
  ns.B = (x) => x
  /** apply函数 */
  ns.apply = (o, ...ps) => new o(...ps)

  ns.awrap = function(x){ return Array.isArray(x) ? x : [x] }

  ns.bool = new (class{})()
  ns.int = new (class{})()
  ns.num = new (class{})()
  ns.str = new (class{})()
  ns.none = new (class{})()
  ns.null = new (class{})()
  ns.undefined = new (class{})()
  ns.fun = new (class{})()

  ns.match = function(v){

    return function(){

      if( arguments.length===0 ) return v     // Corner处理

      // ======== Else处理
      let other = v, N = arguments.length, last = arguments[arguments.length-1]
      if( !Array.isArray(last) ){
        N--
        other = last
      }

      // 迭代每个输入参数
      let i = 0
      while( i < N ){

        const param = arguments[i]    // 获取参数

        if( Array.isArray(param) && param.length > 1 ){   // [criteria, process]

          const criteria = param[0], typ = typeof v   // 获取条件

          if( 
            (criteria===ns.bool && typ==="boolean") ||
            (criteria===ns.int && v%1===0 ) ||
            (criteria===ns.num && typ==="number") ||
            (criteria===ns.str && typ==="string") ||
            (criteria===ns.none && (typ==="null" || typ==="undefined"))
          ){
            return typeof param[1]==="function" ? param[1](v) : param[1]
          }

          if( typeof criteria === "function" ){   // 函数类型处理

            const idx = criteria(v)
            console.assert( idx%1==0, `'criteria'函数的返回值必须为整数。当前值：${idx}` )
            const proc = param.at( idx % (param.length-1) + 1 )
            return typeof proc==="function" ? proc(v) : proc
          }

        }else if( typeof param === "function" ){  // (criteria) => {proc}



        }

        i++   // 参数序号递增
      }

      return typeof other==="function" ? other(v) : other  // 默认返回值
    }

  }

})(window.XLIB||{});




//============================================================================================================================
// DOM
//============================================================================================================================
;(function(ns){

  let dom = ns.DOM || {};

  ;(function Dom(dom){

    dom._INNER_XML_ = function(innerXML){ this.innerXML = innerXML }
    dom.XML = function(innerXML){ return new dom._INNER_XML_(innerXML) }
    dom._INNER_CMD_ = function(replace=true){ this.replace = replace }
    dom.CMD = function(replace){ return new dom._INNER_CMD_(replace) }
    
    /** 返回一个构建的DOM树
     * Format: html(<command>, tagName, <parent>, <attris>, <command>, ...child)
     * 
     * command: {CMD} ---- 插入命令。命令格式：CMD(replace:bool)
     *   - replace: {bool} ---- 替换父元素的已有的元素
     * tagName: {str} ---- Tag名
     *   - 支持任意的tag名称。从而构建标准XML树
     *   - 支持namespace。格式: {namespace}tagName。例如: {www.svg.org}line
     *   - 支持层次化定义。格式：parentTagName>childTagName。例如：html('td > input')   ---- <td><input></input></td>
     *   - 支持无根构建。当tagName===""时，表示无Root。这时，attris设置到parent中, parent必须为有意义的参数，child直接插入到parent。并且返回值为parent
     * parent: {str|DomNode} ---- 插入到的父节点
     *   - str: 父节点id标识
     *   - DomNode: 父节点
     *   - other: 忽略该参数。不插入
     * attris: {dict}  ---- 属性dict
     *   - other: 无属性插入
     *   - 对于某个属性: (key, value)
     *     - 若value=null或者value=undefine: 不插入该属性
     *   - 判断属性的条件：非字符串类型 && 非Array类型 && 非DOM类型
     * ...child: [{DomNode|list[DomNode]|str|XML}]  ---- 子节点(列表)
     *   - DomNode: 添加的节点Element
     *   - str|number: 添加的TextNode
     *   - XML(innerXML:str): 解析innerXML为DOM，然后插入
     *   - list[DomNode]: 允许循环添加的节点Element
     *   - list[list...list[DomNode]]
     * return: {DomNode} ---- 构建的DOM树根节点
     * 
     * Note:
     *   - 连续2个{str}被判定为：tagName + parent
     *   - 第二个参数为{DomNode}被判定为：parent
     *   - children需要{}隔离
     * 
     * Example:
     *   - html('legend', {}, "Topology")   // <legend>Topology</legend>
     *   - html('div', {}, html("p", {}, "Hello"))    // <div><p>Hello</p></div>
     *   - html('fieldset', "control", ...html())       // 在id=control中创建fieldset元素，并添加子元素
     */
    dom.html = function(){
      // console.log(arguments)
      // 函数域变量定义
      let nxtPos = 0, narg = arguments[nxtPos], parent = null, rtn = null, ele = null, replace = false, noRoot = false
    
      // ========  Command处理
      const c_arg = arguments[nxtPos]
      if( c_arg instanceof dom._INNER_CMD_ ){           // 命令类
        if( c_arg.replace ){ replace = true }       // Replace模式
        nxtPos += 1                                 // 解析指针移动
      }
    
      // ========  当前元素创建
      // Namespace解析
      const ele_string = arguments[nxtPos]
      console.assert( typeof ele_string === "string" )    // 输入参数错误过滤
      if( ele_string === "" ){
        noRoot = true
      }else{
        const rst = ele_string.match(/({([^}]*)})?(.*)/)    // \2为namespace；\3为tagNameList
        const isNS = rst[2] ? true : false                  // 是否有Namespace标识判断。true: 有namespace
    
        const eles = rst[3].split('>')                   // 层次化切割
          .map( x => x.trim() )             // 去掉空格
          .filter( x => x!="" )             // 去空
          .map( x => isNS ? document.createElementNS(rst[2], x) : document.createElement(x) )   // 创建元素
    
        eles.reduce( (p,c) => { if(p) p.appendChild(c); return c; }, null )   // 父子挂链
          
        rtn = eles.at(0), ele = eles.at(-1)
      }
      nxtPos += 1   // 必须存在当前节点项
      // console.log(`html: ele_string=${ele_string}; noRoot=${noRoot}; rtn=${rtn}; ele=${ele}; nxtPos=${nxtPos};`)
    
    
      // ========  父元素识别
      const p_arg = arguments[nxtPos]
      if( typeof p_arg === "string" ){              // 父元素以“id”形式呈现
        parent = document.getElementById(p_arg)
      }else if( narg?.nodeType==1 ){                // 父元素以ElementNode形式呈现。第二个参数为ElementNode
        parent = p_arg
      }
      if( parent ){                                 // 过滤掉narg不是父元素id的应用
        
        if( replace ){          // Replace模式，剔除原有DOM元素
          const childs = parent.childNodes
          for( let i=childs.length-1; i>=0; i-- ) parent.removeChild( childs[i] )   // Note: 这里必须为倒序，否则删除的就是间隔的元素
        }
    
        if( noRoot ){   // 无根创建
          rtn = parent, ele = parent
        }else{
          parent.appendChild(rtn)     // 把当前根元素插入到父元素
        }
    
        nxtPos += 1
      }
      // console.log(`html: p_arg=${p_arg}; parent=${parent};`)
    
      console.assert( (noRoot && parent) || !noRoot, `html: 无根创建特性下，parent必须有效。当前输入参数: ${arguments}` )
    
      // ========  属性添加
      let attris = arguments[nxtPos]
      if( arguments.length <= 1 ){
      }else if( typeof attris !== "string" && typeof attris === "object" && !Array.isArray(attris) && !(attris.nodeType==1) ){   // 非字符串和Node类型
        for( let key in attris ){
          if( attris[key] === null || attris[key] === undefined ) continue    // 无效属性不添加
          ele.setAttribute(key, attris[key])
        }
        nxtPos += 1
      }
    
    
      // ========  添加子元素
      function appendChild(p, lst){
        for( let c of lst ){
          if( typeof c === "string" ){    // c为string。添加TextNode
            text = document.createTextNode(c)
            p.appendChild(text)
          }else if( typeof c === "number" ){  // c为number。添加TextNode
            text = document.createTextNode(`${c}`)
            p.appendChild(text)
          }else if( Array.isArray(c) ){   // c为array。内部包含子DOM Element
            appendChild( p, c )
          }else if( c == null || c == undefined ){
              // null、undefined表示不需要添加
          }else if( c.nodeType && c.nodeType==1 ){    // c为DOM Element
            p.appendChild(c)
          }else if( c instanceof dom._INNER_XML_ ){
            p.insertAdjacentHTML('beforeend', c.innerXML)
          }else{
            console.error(`html函数: 接收到不支持的子元素参数类型!`)
            console.error(`    接收到的参数值为：`)
            console.error(c)
            console.error(`    完整的html函数参数列表为：`)
            console.error(arguments)
          }
        }
      }
    
      appendChild( ele, Array.from(arguments).slice(nxtPos) )    // 添加子元素操作
    
      return rtn  // 返回当前元素DOM
    }
    
    /** 获取被选中的radio
     * name: {str} ---- radio组name
     * return: {DomNode} ---- 被选中的radio
     */
    dom.getRadio = function(name){
      const selector = `input[type='radio'][name='${name}']`
      for( let rdo of document.querySelectorAll(selector) ){
        if( rdo.checked )
          return rdo
      }
      // console.log(`getRadio: Cannot find radio element: |${name}|`)
      return null
    }
    
    /**
     * 切换DOM的显示
     * @param {str} name DOM标识
     * @param {str} showType 显示类型
     *   - "visible": 反转div的visibility属性
     *   - other: 反转div的diaplay属性	
     */
    dom.toggle = function(name, showType="block"){
      const ele = document.getElementById(name)
      if( ele ){
        if( showType == "visible" ){
          if( ele.style.visibility === "visible" ) ele.style.visibility = "hidden"
          else ele.style.display = "visible"
        }else{
          if( ele.style.display === "none" ) ele.style.display = showType
          else ele.style.display = "none"
        }
      }
    }

    /**
     * 设置Radio组
     * @param {str} name Radio name属性
     * @param {str} value Radio对应的value属性。与次相符的被选中 
     * @returns 
     */
    dom.setRadio = function(name, value){
      const selector = `input[type='radio'][name='${name}']`
      const rdos = document.querySelectorAll(selector)
      rdos.forEach( (rdo, i) => {
        if( rdo.value == value ) rdo.checked = true
        else rdo.removeAttribute("checked")    
      })
      return null
    }

    /**
     * 设置Select.option
     * @param {str} name Select的id属性
     * @param {str} value Select对应的innerText属性。与次相符的被选中 
     * @returns 
     */
    dom.setSelect = function(name, value){
      const select = document.getElementById(name)
      for( let opt of select.querySelectorAll(`option`) ){
        if( opt.innerText == value )  opt.setAttribute("selected", true)
        else opt.removeAttribute("selected")
      }
    }

    dom.setCheckbox = function(id, value){
      const chk = document.getElementById(id)
      if( value ){
        chk.checked = true
			}else{
        chk.removeAttribute("checked")
			}
		}

    dom.setInput = function(id, value){
      const ipt = document.getElementById(id)
      const t = ipt.tagName
      if( t=="input" ){
        const typ = ipt.getAttribute("type")
				if( typ == "checkbox" )
				  dom.setCheckbox(id, value)
			}else if( t=="select" ){
        dim.setSelect(id, value)
			}
		}

    

  })(dom);

  ns.DOM = dom

})(window.XLIB||{});



// ################################################################################################
//  Math
// ################################################################################################
;(function(ns){

  let math = ns.Math || {};

  ;(function math(math){

    /**
     * 求数组的加权求平均
     * @param {Array} arr 输入数据
     * @param {(v,i)=>number} vfunc 数据量计算
     * @param {(v,i)=>number|null} wfunc 权重计算。忽略默认使用等权重计算
     */
    math.wavg = function(arr, vfunc, wfunc=null){
      let wgt = 0, sum = 0
      for( let i=0; i<arr.length; i++ ){
        const w = wfunc ? wfunc(arr[i], i) : 1
        if( w == 0 ) continue 
        wgt += w 
        sum += vfunc(arr[i], i)
      }
      return wgt==0 ? 0 : sum/wgt
    }

    math.recycle = function(lst){
      const rst = [lst.slice()]
      for( let n=1; n<lst.length; n++ ){
        const new_lst = rst[n-1].slice(1)
        new_lst.push( rst[n-1][0] )
        rst.push(new_lst)
      }
      return rst
    }

    /**
     * 生成集合所有的排列情况
     * @param {Array} lst
     * @return {List[Array]} 
     */
    math.permutation = function(lst){

      // 输入异常处理
      if( !Array.isArray(lst) ) return [[]]
      // 特殊处理
      if( lst.length==1 ) return [lst]

      // 递归处理
      const rst = (function recur( pre_list, post_list ){
        if( post_list.length == 1 )   // 迭代到叶子
          return pre_list.concat(post_list)   // 返回新完整列表
        else{
          let rst = []
          for( let i=0; i<post_list.length; i++ ){  // 循环每个post_list变量，分别做“头”
            const r = recur( pre_list.concat([post_list[i]]), post_list.slice(0,i).concat(post_list.slice(i+1)) )
            rst.push(r)
          }
          return rst
        }
      })([], lst)
    
      return rst.flat(lst.length-2)
    }

    math.eq = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] != rst ) return false
      return true
    }
    math.neq = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] == rst ) return false
      return true
    }
    math.gt = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] <= rst ) return false
      return true
    }
    math.ge = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] < rst ) return false
      return true
    }
    math.lt = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] >= rst ) return false
      return true
    }
    math.le = (...x) => {
      let rst = x[0]
      for(let i=1; x<x.length; i++) if( x[i] > rst ) return false
      return true
    }
    /** 
     * 比较函数
     * @param {number} x 第一个操作数
     * @param {number} y 第二个操作数
     * @return 1: x>y; 0: x=y; -1: x<y;
     */
    math.cmp = (x,y) => x>y ? 1 : x<y ? -1 : 0

    math.add = (a, ...x) => x.reduce((l,c) => l+c, a)  // 连续加
    math.sub = (a, ...x) => x.reduce((l,c) => l-c, a)  // 连续减
    math.mul = (a, ...x) => x.reduce((l,c) => l*c, a)  // 连续乘
    math.div = (a, ...x) => x.reduce((l,c) => l/c, a)  // 连续除
    math.mod = (a, ...x) => x.reduce((l,c) => l%c, a)  // 连续余
    math.div2 = (...x) => x.reduce((l,c) => [l[0].push(Math.floor(l[1]/c)), l[0]%c], [[], x[0]])

    ns.Math = math

  })(math);

})(window.XLIB||{});
